package com.het.monitor.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.het.monitor.common.AreaType;
import com.het.monitor.config.Apps;
import com.het.monitor.service.AlertAppService;
import com.het.monitor.service.AppConfService;
import com.het.monitor.task.model.AlertApp;
import com.het.monitor.task.model.AppConf;
import com.het.monitor.util.AESUtil;
import com.het.monitor.util.ConnLinuxServerUtil;
import com.het.monitor.util.DateUtils;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ScheduleJobMonitorA {
    @Resource
    AlertAppService alertAppService;
    @Resource
    AppConfService appConfService;

    @Value("${aes.secret}")
    public String secret;

    @Async
//    @Scheduled(fixedDelay = 1000 * 90)
    @XxlJob("monitorAJobHandler")
    public void task() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        log.info("执行监控任务A开始时间：{}", DateUtils.getNowTime(DateUtils.YYY_MM_DD_HH_MM));

        try {
            // 1. 查询配置
            List<AppConf> appConfList = queryAppConfigs();

            // 2. 监控应用状态
            MonitorResult result = monitorApps(appConfList);

            // 3. 处理监控结果
            handleMonitorResult(result);

        } catch (Exception e) {
            log.error("监控任务A执行异常", e);
        } finally {
            log.info("执行监控任务A结束时间：{}, 耗时：{}ms",
                    DateUtils.getNowTime(DateUtils.YYY_MM_DD_HH_MM),
                    System.currentTimeMillis() - startTime);
        }
    }

    private List<AppConf> queryAppConfigs() {
        QueryWrapper<AppConf> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("area_type", AreaType.A)
                .eq("status", 1)
                .orderByAsc("app_jar");
        return appConfService.list(queryWrapper);
    }

    private MonitorResult monitorApps(List<AppConf> appConfList) {
        if (CollectionUtils.isEmpty(appConfList)) {
            return new MonitorResult(Collections.emptyList(), false);
        }

        // 并行流处理监控，提高效率
        Map<String, Integer> monitorStopAppMap = appConfList.stream()
                .collect(Collectors.toMap(
                        AppConf::getAppJar,
                        AppConf::getStopNum,
                        (existing, replacement) -> existing));

        Map<String, AtomicInteger> monitorStopedNumMap = new ConcurrentHashMap<>();
        List<AlertApp> alertAppList = Collections.synchronizedList(new ArrayList<>());
        //并行处理（多线程）
        appConfList.parallelStream().forEach(app -> {
            ConnLinuxServerUtil connLinuxServerUtil = new ConnLinuxServerUtil();
            boolean monitorResult = connLinuxServerUtil.monitor(connLinuxServerUtil, app, app.getCheckCmd(), AreaType.A, secret);
            if (!monitorResult) {
                alertAppList.add(buildAlertApp(app));
                monitorStopedNumMap.computeIfAbsent(app.getAppJar(), k -> new AtomicInteger(0)).incrementAndGet();
            }
        });

        boolean shouldSwitch = monitorStopedNumMap.entrySet().stream()
                .anyMatch(entry -> entry.getValue().get() >= monitorStopAppMap.get(entry.getKey()));

        return new MonitorResult(alertAppList, shouldSwitch);
    }

    private AlertApp buildAlertApp(AppConf app) {
        return AlertApp.builder()
                .ip(app.getIp())
                .appJar(app.getAppJar())
                .appName(app.getAppName())
                .area_type(app.getAreaType())
                .build();
    }

    private void handleMonitorResult(MonitorResult result) {
        // 保存报警信息
        if (!CollectionUtils.isEmpty(result.getAlertApps())) {
            alertAppService.saveOrUpdateBatch(result.getAlertApps());
        }

        // 执行切换逻辑
        if (result.isShouldSwitch()) {
            stopAreaA();
            startAreaB();
        }
    }

    private void stopAreaA() {
        log.info("监控A域有异常，执行A域停止命令！");
        List<AppConf> parentApps = queryParentApps(AreaType.A);
        parentApps.forEach(app -> executeCommand(app, "A域停止"));
    }

    private void startAreaB() {
        log.info("监控A域有异常，执行B域启动命令！");
        ConnLinuxServerUtil util = new ConnLinuxServerUtil();
        List<AppConf> parentApps = queryParentApps(AreaType.B);
        parentApps.stream()
                .filter(app -> !util.monitor(util, app, app.getCheckCmd(), AreaType.B, secret))
                .forEach(app -> executeCommand(app, "B域启动"));
    }

    private List<AppConf> queryParentApps(String areaType) {
        QueryWrapper<AppConf> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("area_type", areaType)
                .eq("parent_id", 0)
                .eq("status", 1);
        return appConfService.list(queryWrapper);
    }

    private void executeCommand(AppConf app, String operation) {
        ConnLinuxServerUtil util = new ConnLinuxServerUtil();
        if (StringUtils.isNotBlank(app.getDoCmd())) {
            Apps con = buildConnection(app);
            String result = util.execute(app.getDoCmd(), con);
            log.info("【{}】命令: {}, IP: {}, 结果: {}", operation, app.getDoCmd(), app.getIp(), result);
        }
    }

    private Apps buildConnection(AppConf app) {
        return Apps.builder()
                .ip(app.getIp())
                .uName(app.getUser())
                .uPwd(AESUtil.decrypt(app.getPwd(), secret))
                .build();
    }

    @Getter
    @AllArgsConstructor
    private static class MonitorResult {
        private final List<AlertApp> alertApps;
        private final boolean shouldSwitch;
    }
}
